﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Psl.Applications;
using Psl.Controls;
using Stl.Controls;
using System.Collections;

namespace Stl.Tme.Components.DesignOutline
{
    /// Dans cette classe, on a classé les méthodes et les evenements par fonctionnalité
    /// <summary>
    /// Ce controle propose un mode de conception
    /// </summary>
    public partial class DesignCluster : UserControl
    {
        // **************************** Champs, Propriétés *******************************//

        // référence sur le service de la barre d'état (composant StatusReporter)
        private IStatusService status;

        private static string COPY_SUFFIX = "_copy";

        // paste = true si dans clipboard contient un control "coupé"
        private bool paste = false;

        // le chemin du fichier à sauvegarder
        public string FilePath
        {
            get;
            private set;
        }

        /// <summary>
        /// Indique si des modifications ont eu lieu après la dernière sauvegarde.
        /// </summary>
        [DefaultValue(false)]
        public bool Dirty
        {
            get;
            private set;
        }

        public static string DESIGN_CLUSTER = "DesignCluster";

        // **************************** Constructeur *******************************//

        public DesignCluster()
        {
            InitializeComponent();

            Registry.MergeInMainMenu(menu);
            Registry.MergeInMainTools(tools);
            status = Registry.MainStatus;

            chatBridge.DesignFrame = designFrame;
        }

        // Affichage d'info
        protected void SetStatusInfo(string str)
        {
            if (status == null) return;
            status.TextLeft = DESIGN_CLUSTER;
            status.TextInfos = str;
        }

        private void OnIdle()
        {
            bool ctrlSelected = designFrame.SelectedControl != null;

            removeAction.Enabled = ctrlSelected;
            clearAction.Enabled = designFrame.Controls.Count != 0;
            saveAction.Enabled = saveAsAction.Enabled = Dirty;
            copyAction.Enabled = cutAction.Enabled = ctrlSelected;
            pasteAction.Enabled = Clipboard.ContainsData(typeof(ControlWrapper).FullName); // enable seulement s'il contient un controlwrapper dans le clipboard
            undoAction.Enabled = designThrash.CanUndo;
            redoAction.Enabled = designThrash.CanRedo;
            tools.Visible = designClusterShowToolStripMenuItem.Checked;

            Point mouseLocation = designFrame.PointToClient(MousePosition);
            status.TextMiddle = "X=" + mouseLocation.X + ",Y=" + mouseLocation.Y;
        }

        private void applicationEvents_ApplicationIdle(object sender, EventArgs e)
        {
            OnIdle();
        }

        // **************************** Add control *******************************//

        protected string MakeName(string pref)
        {
            int count = 0;
            string name;
            do
            {
                count++;
                name = pref + count;
            } while (designFrame.Controls.ContainsKey(name));
            return name;
        }

        private void designFrame_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                DesignToolbox.ControlData cd = designToolbox.SelectedControl;
                if (cd == null)
                    return;

                AddControl(cd.Type, e.X, e.Y);
                designToolbox.UnSelectAll();
            }
        }

        private Control AddControl(Type type, int x, int y)
        {
            try
            {
                Control instance = (Control)type.GetConstructor(Type.EmptyTypes).Invoke(new Object[0]);
                instance.Name = MakeName(type.Name);
                instance.KeyDown += OnControlKeyDown;
                instance.Location = new Point(x, y);
                designFrame.Controls.Add(instance);
                instance.Focus();

                // selection
                designFrame.SelectedControl = instance;
                // trash
                designThrash_Add_AddControlCommand(instance);
                // status
                SetStatusInfo("Control " + type.Name + " créé : " + instance.Name);

                return instance;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private void OnControlKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                RemoveControl(designFrame.SelectedControl);
            }
        }

        private void designFrame_ControlAdded(object sender, ControlEventArgs e)
        {
            selection.Items.Add(e.Control.Name);
            Dirty = true;
        }

        private void designFrame_ControlRemoved(object sender, ControlEventArgs e)
        {
            selection.Items.Remove(e.Control.Name);
            Dirty = true;
        }

        private void selection_SelectedIndexChanged(object sender, EventArgs e)
        {
            designFrame.SelectedControl = designFrame.Controls[selection.SelectedItem.ToString()];
        }

        private void designFrame_SelectedControlChanged(object sender, EventArgs e)
        {
            Control control = designFrame.SelectedControl;
            if (control != null)
            {
                selection.SelectedItem = control.Name;
                propertyGrid.SelectedObject = control;
            }
        }

        // **************************** Drag & Drop *******************************//

        private void designFrame_DragEnter(object sender, DragEventArgs e)
        {
            if(e.Data.GetDataPresent(typeof(DesignToolbox.ControlData)))
                e.Effect = DragDropEffects.Move;
        }

        private void designFrame_DragDrop(object sender, DragEventArgs e)
        {
            DesignToolbox.ControlData control = e.Data.GetData(typeof(DesignToolbox.ControlData))
                                                as DesignToolbox.ControlData;
            int x = e.X; 
            int y = e.Y;

            Point pos = designFrame.PointToClient(new Point(e.X, e.Y));
            AddControl(control.Type, pos.X, pos.Y);
            designToolbox.UnSelectAll();
        }

        // **************************** Remove & Clear *******************************//
        private void removeAction_Execute(object sender, EventArgs e)
        {
            RemoveControl(designFrame.SelectedControl);
        }

        private void clearAction_Execute(object sender, EventArgs e)
        {
            ClearControls();
        }

        private void RemoveControl(Control c)
        {
            if (c == null) return;
            designFrame.Controls.Remove(c);
            // thrash
            designThrash_Add_RemoveControlCommand(c);
            // status
            SetStatusInfo("Control " + c.Name + " supprimé");
        }

        private void ClearControls()
        {
            ControlCollection controls = designFrame.Controls;
            Control[] tabCtrls = new Control[controls.Count];
            for (int i = 0; i < controls.Count; i++)
                tabCtrls[i] = controls[i];
            controls.Clear();
            // thrash
            designThrash_Add_ClearControlCommand(tabCtrls);
            // status
            SetStatusInfo("Effacé");
        }

        // **************************** Copier , Couper & Coller *******************************//
        protected void DoCopy()
        {
            DesignSerializer.ControlToClipboard(designFrame.SelectedControl);
            paste = false;
            // status
            SetStatusInfo("Copié");
        }

        protected void DoCut()
        {
            Control ctrl = designFrame.SelectedControl;
            if (ctrl != null)
            {
                DesignSerializer.ControlToClipboard(ctrl);
                RemoveControl(ctrl);
                paste = true;
                // status
                SetStatusInfo("Coupé");
            }            
        }

        protected void DoPaste()
        {
            try
            {
                Control ctrl = DesignSerializer.ClipboardToControl();
                if (ctrl != null)
                {
                    Point location = designFrame.LastClickLocation;
                    PasteControl(ctrl, location.X, location.Y);
                    if (paste)
                        ClearClipBoard();
                    // status
                    SetStatusInfo("Collé");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

        private void copyAction_Execute(object sender, EventArgs e)
        {
            DoCopy();
        }

        private void cutAction_Execute(object sender, EventArgs e)
        {
            DoCut();
        }

        private void pasteAction_Execute(object sender, EventArgs e)
        {
            DoPaste();
        }

        private void PasteControl(Control ctrl, int x, int y)
        {
            if (ctrl == null) return;
            if (designFrame.Controls.ContainsKey(ctrl.Name))
            {
                ctrl.Name = MakeName(ctrl.Name + COPY_SUFFIX);
            }
            ctrl.Location = new Point(x, y);
            designFrame.Controls.Add(ctrl);
            // selection
            designFrame.SelectedControl = ctrl;
            // thrash
            designThrash_Add_AddControlCommand(ctrl);
            // status
            SetStatusInfo("Control " + ctrl.GetType().Name + " créé : " + ctrl.Name);
        }

        private void ClearClipBoard()
        {
            Clipboard.Clear();
            paste = false;
        }

        // **************************** New, Open, Save & Save as *******************************//

        private void newAction_Execute(object sender, EventArgs e)
        {
            DoNew();
        }

        private void openAction_Execute(object sender, EventArgs e)
        {
            DoOpen();
        }

        private void saveAction_Execute(object sender, EventArgs e)
        {
            DoSave();
        }

        private void saveAsAction_Execute(object sender, EventArgs e)
        {
            DoSaveAs();
        }

        private void designFrame_SelectedControlEndDrag(object sender, EventArgs e)
        {
            Dirty = true;
        }

        private void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            Dirty = true;
            // thrash
            designThrash_Add_PropertyChangeCommand(propertyGrid.SelectedObject, e.OldValue, e.ChangedItem.PropertyDescriptor);
        }

        private void designThrash_Add_PropertyChangeCommand(object component, object oldValue, PropertyDescriptor propertyDescriptor)
        {
            designThrash.Add(new PropertyChangeCommand(component, oldValue, propertyDescriptor));
        }

        /// <summary>
        /// Demande de sauvegarder si il y a des modifications.
        /// </summary>
        private void DoNew()
        {
            // Demander de sauvegarde si modifier
            // ne cree pas de nouveau fichier si utilisateur demande d'annuler
            if (!ConfirmDoSave())
                return;
            this.Clear();
            designThrash.RedoStack.Clear();
            designThrash.UndoStack.Clear();
            // status
            SetStatusInfo("Nouvelle surface de conception créée");
        }

        private void DoOpen()
        {
            if (DialogResult.OK == openFileDialog.ShowDialog())
            {
                Control[] controls = DesignSerializer.FileToControls(openFileDialog.FileName);
                if (controls == null)
                {
                    MessageBox.Show("Format inconnu");
                    // status
                    SetStatusInfo("Error : Format inconnu");
                }
                else
                {
                    FilePath = openFileDialog.FileName;
                    designFrame.Controls.Clear();
                    designFrame.Controls.AddRange(controls);
                    Dirty = false;
                }
            }
        }

        // filepath != ""
        private void DoWriteFile()
        {
            DesignSerializer.ControlsToFile(FilePath, designFrame.Controls);
            Dirty = false;
            // status
            SetStatusInfo("Sauvegardé dans le fichier : " + FilePath);
        }

        // Demande de confirmation de sauvegarde.
        private bool ConfirmDoSave()
        {
            if (Dirty)
            {
                DialogResult res = MessageBox.Show("Voulez-vous sauvegarder les modifications?",
                                                   "Sauvegarder?",
                                                   MessageBoxButtons.YesNoCancel);
                switch (res)
                {
                    case DialogResult.Yes:
                        DoSave();
                        return true;
                    case DialogResult.No:
                        return true;
                    case DialogResult.Cancel:
                        return false;
                    default :
                        return true;
                }
            }
            else
                return true;
        }

        /// <summary>
        /// Permet de sauvegarder le document.
        /// </summary>
        private void DoSave()
        {
            if (FilePath == null || FilePath == "")
                DoSaveAs();
            else
                DoWriteFile();
        }

        /// <summary>
        /// Déclenche le dialog d'enregistrement.
        /// </summary>
        private void DoSaveAs()
        {
            if (DialogResult.OK == saveFileDialog.ShowDialog())
            {
                FilePath = saveFileDialog.FileName;
                DoWriteFile();
            }
        }

        /// <summary>
        /// Supprime tous les composants de la surface de design.
        /// </summary>
        private void Clear()
        {
            designFrame.Controls.Clear();
            designToolbox.UnSelectAll();
            propertyGrid.SelectedObject = null;
            FilePath = "";
            Dirty = false;

        }

        // **************************** Fermeture *******************************//

        private void applicationEvents_ApplicationClosing(object sender, FormClosingEventArgs e)
        {
            // demande si on veut sauvegarder a la fermeture si modifié
            if (!ConfirmDoSave())
                e.Cancel = true;
        }

        // **************************** Undo & Redo *******************************//

        private void undoAction_Execute(object sender, EventArgs e)
        {
            designThrash.Undo();
        }

        private void redoAction_Execute(object sender, EventArgs e)
        {
            designThrash.Redo();
        }


        private void designThrash_TrashChanged(object sender, EventArgs e)
        {
            RefreshUndoRedoSplitButton();
        }

        // raffraichir les listes de split button de UndoRedo
        private void RefreshUndoRedoSplitButton()
        {
            ToolStripItemCollection items = undoToolStripSplitButton.DropDownItems;
            items.Clear();
            foreach (IUndo cmd in designThrash.UndoStack)
                items.Add(cmd.GetCommand()); 

            items = redoToolStripSplitButton.DropDownItems;
            items.Clear();
            foreach (IRedo cmd in designThrash.RedoStack)
                items.Add(cmd.GetCommand());
        }

        private void designThrash_Add_AddControlCommand(Control control)
        {
            designThrash.Add(new AddControlCommand(designFrame, control));
        }

        private void designThrash_Add_RemoveControlCommand(Control control)
        {
            designThrash.Add(new RemoveControlCommand(designFrame, control));
        }

        private void designThrash_Add_ClearControlCommand(Control[] tabCtrls)
        {
            designThrash.Add(new ClearCommand(designFrame, tabCtrls));
        }

        private void designFrame_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete)
            {
                RemoveControl(designFrame.SelectedControl);
            }
        }

        // **************************** Archivage *******************************//
        private void applicationEvents_Archive(IArchiver sender)
        {
            OnArchive(sender);
        }

        private void OnArchive(IArchiver sender)
        {
            sender.PushSection("DesignClusterPlugin.DesignCluster");
            try
            {
                sender.ArchiveProperty("leftPanel.Width", leftPanel, "Width", 150);
                sender.ArchiveProperty("propertyGrid.Width", propertyGrid, "Width", 200);
            }
            finally { sender.PopSection(); }

        }

        // **************************** Divers *******************************//
        private void designFrame_MouseMove(object sender, MouseEventArgs e)
        {
            if (designToolbox.SelectedControl != null)
                designFrame.Cursor = designToolbox.SelectedControl.Cursor;
            else designFrame.Cursor = DefaultCursor;
        }

        private void aboutAction_Execute(object sender, EventArgs e)
        {
            (new DesignAboutBox()).ShowDialog();
        }

        // menu
        private void designClusterShowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            designClusterShowToolStripMenuItem.Checked = !designClusterShowToolStripMenuItem.Checked;
        }
    }
}
